package universe;

import eventManager.EventManager;
import galaxies.*;
import galaxies.enums.EGalaxyType;
import galaxies.enums.SpiralArmNumbers;
import gameAppLayer.GameSettings;
import gameAppLayer.XMLData;
import gameAppLayer.ExternalData;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.vecmath.Vector3d;

import old.coordinate.CoordinateController;
import old.coordinate.CoordinateGenerator;
import old.coordinate.CoordinateModel;


import com.jme3.asset.AssetKey;
import com.jme3.asset.AssetManager;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;

import controller.AbstractController;
import controller.IController;
import controls.IMasterControl;


import model.PhysicalDataModel;
import stars.BatchSystemBuilder;
import stars.StarController;
import stars.StarModel;
import stars.starSystems.StarSystemController;
import stars.starSystems.StarSystemGenerator;
import stars.starSystems.StarSystemModel;
import utilities.HexTileGen;
import utilities.Key;

/*
 * Factory that creates the initial game universe by calling other factories
 * Returns a list of controllers for all items in the game universe.  
 * 
 * Inputs:  UniverseSettings, AssetManager
 * Outputs:  List of controllers for all game universe objects.
 * 
 */
public class UniverseBuilder {
	
	private static final Logger logger = Logger.getLogger(UniverseBuilder.class.getName());
	
	public Random rand = new Random();
	public GameSettings settings;
	public AssetManager assetManager;
	public ExternalData externalData;
	public int universeLength;
	public int minStars; //varies by galaxy type
	public int maxStars = 10000; //hard ceiling	
	public float avgLength;
	
	public ArrayList<IMasterControl> masterList = new ArrayList<IMasterControl>();
	public ArrayList<IMasterControl> systemControllerList = new ArrayList<IMasterControl>();	
	public ArrayList<IMasterControl> galaxyControllerList = new ArrayList<IMasterControl>();
	
//	public ArrayList<CoordinateController> coordinates = new ArrayList<CoordinateController>();
	//public ArrayList<CStarSystem> systems = new ArrayList<CStarSystem>();
	//public ArrayList<CStar> stars = new ArrayList<CStar>();
	
	public ArrayList<ArrayList<Integer>> locations = new ArrayList<ArrayList<Integer>>();
	//public ArrayList<Vector2f> extraLocations = new ArrayList<Vector2f>();
	
	public UniverseBuilder(
			GameSettings settings, 
			ExternalData externalData)
	{
		this.settings = settings;
		this.externalData = externalData;
	}
	
	
	
	//Creates the entire universe model
	public ArrayList<IMasterControl> createUniverse() {
		
		logger.log(Level.INFO, "FUniverse: createUniverse() entered");
		
		//UNIVERSE
		//-------------------------------------------------------
		//MUniverse universe = new MUniverse(settings);
		//-------------------------------------------------------
		
		//GALAXY POSITIONS
		//-------------------------------------------------------
		logger.log(Level.INFO, "FUniverse: createUniverse(); Generating tiles");
		//Calculate individual galaxy dimensions (no. of tiles)
		GalaxySizeCalculator galSizeCalc = new GalaxySizeCalculator();
		HashMap<String, ArrayList<ArrayList<Integer>>> galaxyDims = galSizeCalc.calcGalaxyDims(settings);		
		HashMap<String, Double> galaxyRotations = galSizeCalc.rotations;
		
		//Calculate overall universe dimensions (no. of tiles)
		/*
		 * Method:  Sum up largest dimension of all galaxies, multiply by scaleFactor to get universe radius
		 * Universe is circular.  Tiles are only created in galaxies or for special intergalactic objects.
		 * When infrastructure or new objects are placed in intergalactic space, new tiles are created to host them.
		 */
		double scaleFactor = 2.0;
		ArrayList<String> refs = settings.getGalaxyRefs();
		ArrayList<ArrayList<Integer>> dims;
		int dimSum = 0; //not chinese food
		int largeDim = 0;
		//sum up largest dimensions of each galaxy
		for (String ref: refs) {
			dims = galaxyDims.get(ref);
			
			largeDim = findLargest(dims);
			
			dimSum += largeDim;
			
//			if (dims.get(0).get(0) >= dims.get(0).get(1)) {
//				dimSum += dims.get(0).get(0); //x
//			}
//			else {
//				dimSum += dims.get(0).get(1); //y
//			}			
		}
		
		int universeRadius = (int) (dimSum*scaleFactor);
		int numUniverseTiles = (int) (universeRadius*universeRadius*Math.PI);
		
		logger.log(Level.INFO, "FUniverse: createUniverse(); UniverseRadius= " + universeRadius);
		logger.log(Level.INFO, "FUniverse: createUniverse(); Number of Tiles= " + numUniverseTiles);
		
		//generate tile coordinates
		//ArrayList<ArrayList<Integer>> universeTileLocations = HexTileGen.genTileLocations(universeRadius);//genTileLocations(universeRadius);
		//-------------------------------------------------------
		
		
		
		//GALAXIES
		//-------------------------------------------------------
		logger.log(Level.INFO, "FUniverse: createUniverse() START GalaxyGen");
		//SpiralArmNumbers armNumbers = externalData.getArmNumbers();
		GalaxyGenerator2 galGen = new GalaxyGenerator2();
		galaxyControllerList = galGen.createGalaxies(
				settings, 
				assetManager,				
				galaxyDims, 
				galaxyRotations, 
				externalData);
		locations.addAll(galGen.locations);
		
		masterList.addAll(galaxyControllerList);
		
		logger.log(Level.INFO, "FUniverse: createUniverse() END GalaxyGen");
		
		System.out.println("Locations Size: " + locations.size());
		
		//-------------------------------------------------------
		
		//EXTRA LOCATIONS (not generated in galaxies above)
		//-------------------------------------------------------
		//Generate extra locations, add to masterList
		
		
		//-------------------------------------------------------
		
		logger.log(Level.INFO, "FUniverse: createUniverse() START Systems, StarGroups, Stars");
		System.out.println("Systems, StarGroups, and Stars!");
		//SYSTEMS for GALACTIC LOCATIONS
		/*
		 * Systems include:
		 * 1) Stars
		 * 2) Habitable objects
		 * 3) 
		 */
		//-------------------------------------------------------
		//BatchSystemBuilder sysDesign = new BatchSystemBuilder(settings, assets, eventManager);
		
		
		
		
		
		/* 
		systemControllerList.addAll(sysDesign.createSystems(locations));
		*/
		
		//StarSystemGenerator sysGen = new StarSystemGenerator(settings, assets, eventManager);
		//systems = sysGen.createStarSystems(locations);
		
		/*
		masterList.addAll(systemControllerList);		
		logger.log(Level.INFO, "FUniverse: createUniverse() END Systems, StarGroups, Stars");
		*/
		//-------------------------------------------------------
		
		//System.out.println("Coordinates!");
		//COORDINATES for EXTRA LOCATIONS
		//-------------------------------------------------------
		//CoordinateGenerator coordGen = new CoordinateGenerator(settings, assets);
		//coordinates = coordGen.createIndependentCoords(extraLocations);
		
		//masterList.addAll(coordinates);		
		//-------------------------------------------------------
		
		//System.out.println("Stars!");
		//FILL IN SYSTEMS WITH STARS, HABITABLES, SPECIALS, ETC.
		//Accomplished in stages via SystemDesigner
		//-------------------------------------------------------		
		//SystemDesigner sysDesign = new SystemDesigner(settings, assets, eventManager);
				
		//STARS
		//stars = sysDesign.createStars(systems);
		//NOTE:  Star system graphic should reflect stars within the system, currently does not.
		
		
		
		
		//HABITABLES (including moons)
		//Must have determined where stars are already 
		//for (each system) { buildHabitables; }
			
		//RESOURCES
		//for (each habitable) { buildResources; }
			
		//SPECIAL ITEMS
		//Etc.
			
		//CACHES
			
		//PUZZLES
			
		//ETC.			
		//-------------------------------------------------------
		
		
		
		//Fill in EXTRA LOCATIONS with objects
		//-------------------------------------------------------
		
		
		//-------------------------------------------------------
		
		return masterList;
	}
	
	
	
	
	
	private int findLargest(ArrayList<ArrayList<Integer>> dims) {
		
		int largest = 0;
		for (ArrayList<Integer> dim: dims) {
			for (Integer number: dim) {
				if (Math.abs(number) > largest) {
					largest = number;
				}				
			}
		}
		
		return largest;
	}



//	private HashSet<Key> extractKeys(ArrayList<PhysicalDataset> inputs) {
//		
//		HashSet<Key> keys = new HashSet<Key>();
//		
//		for (PhysicalDataset model: inputs) {
//			keys.add(model.getKey());
//		}
//		
//		return keys;		
//	}	
		
		
}
